#include "res/res_texture_mgr.h"
#include "res/res_font.h"

#include "frame.h"
#include "tex/tex_bmp.h"
#include "material.h"
#include "timer.h"
#include "math/m_intersection.h"
#include "projection.h"

#define GAP_WIDTH	0.02

G3::FONT Font1;
G3::MATERIAL Mat1, Mat2;
G3::TEXTBOX TextBox, TextBox2;
G3::POLYGON3F Polygon1, Frag1, Frag2;
G3::INTERSECTION3F I [2];
G3::PLANE3F SplitPlane;
G3::VIEW View;
G3::RAY3F Ray [2];
uint RayID = 0;

G3::FRAME frame (640, 480, 24, 0);

G3::FPS_COUNTER FPSCounter;
G3::TIMER MovementTimer;

void INSUpdateHandler (G3::INPUT *Input, float aElapsedTime) {
	if (Input->Keyboard.IsKeyDown (SDLK_a))
		Input->Frame->Renderer.Camera.Move (G3::VEC3F (0.0f, 1.0f, 0.0f) * aElapsedTime);
	if (Input->Keyboard.IsKeyDown (SDLK_z))
		Input->Frame->Renderer.Camera.Move (G3::VEC3F (0.0f, -1.0f, 0.0f) * aElapsedTime);
	if (Input->Keyboard.IsKeyDown (SDLK_LEFT))
		Input->Frame->Renderer.Camera.Move (G3::VEC3F (-1.0f, 0.0f, 0.0f) * aElapsedTime);
	if (Input->Keyboard.IsKeyDown (SDLK_RIGHT))
		Input->Frame->Renderer.Camera.Move (G3::VEC3F (1.0f, 0.0f, 0.0f) * aElapsedTime);
	if (Input->Keyboard.IsKeyDown (SDLK_UP))
		Input->Frame->Renderer.Camera.Move (G3::VEC3F (0.0f, 0.0f, 1.0f) * aElapsedTime);
	if (Input->Keyboard.IsKeyDown (SDLK_DOWN))
		Input->Frame->Renderer.Camera.Move (G3::VEC3F (0.0f, 0.0f,-1.0f) * aElapsedTime);

	if (Input->Keyboard.IsKeyDown (SDLK_ESCAPE))
		Input->SendQuit ();
}

void INSMouseClickHandler (G3::INPUT *Input, SDL_Event *Event, float aElapsedTime) {
	G3::RAY3D Rayd;
	G3::VEC2I MPos;
	int i;

	if (Event->type == SDL_MOUSEBUTTONDOWN &&
		Event->button.button == SDL_BUTTON_LEFT) {

		SDL_GetMouseState (&MPos.x, &MPos.y);

		Rayd = G3::ProjectPerspective (MPos);

		Ray [RayID].a.x = (float) Rayd.a.x;
		Ray [RayID].a.y = (float) Rayd.a.y;
		Ray [RayID].a.z = (float) Rayd.a.z;
		Ray [RayID].b.x = (float) Rayd.b.x;
		Ray [RayID].b.y = (float) Rayd.b.y;
		Ray [RayID].b.z = (float) Rayd.b.z;

		if (RayID == 1) {
			RayID = 0;

			I [0] = G3::RayPlaneIntersection (Polygon1.Plane, Ray [0]);
			I [1] = G3::RayPlaneIntersection (Polygon1.Plane, Ray [1]);

			if (!G3_TESTFLAG (I [0].Flags, G3::INTERSECTION::INT_LINE_FRAG) ||
                !G3_TESTFLAG (I [1].Flags, G3::INTERSECTION::INT_LINE_FRAG))
				return;
			// The user just double-clicked - the plane is undetermined
			if (I [0].Point == I [1].Point)
				return;

			SplitPlane = G3::PLANE3F (I [0].Point, I [1].Point, I [0].Point + Polygon1.Plane.n);
			G3::MainLog.Report ("Plane: (%f %f %f) %f\n", SplitPlane.n.x, SplitPlane.n.y, SplitPlane.n.z, SplitPlane.d);

			G3::SplitPolygonByPlane (Polygon1, SplitPlane, &Frag1, &Frag2);

			// Make a small gap between these two fragments
			for (i=0; i<(int) Frag1.Vertices.size (); i++) {
				Frag1.Vertices [i] += SplitPlane.n * GAP_WIDTH;
				Frag1.Vertices [i] -= Polygon1.Plane.n * 0.001;
			}

			for (i=0; i<(int) Frag2.Vertices.size (); i++) {
				Frag2.Vertices [i] -= SplitPlane.n * GAP_WIDTH;
				Frag2.Vertices [i] -= Polygon1.Plane.n * 0.001;
			}
		} else
			RayID++;
	}
	else if (Event->type == SDL_MOUSEBUTTONDOWN &&
			Event->button.button == SDL_BUTTON_RIGHT) {

		if (Input->Mouse.Mode == MOUSE_NORMAL)
			Input->Mouse.SetMode (MOUSE_CENTER);
		else
			frame.Input.Mouse.SetMode (MOUSE_NORMAL);
	}
}

int main (int argc, char **argv) {
	int i = 0;

	frame.Create ("Game3ngine");

	frame.Renderer.Create ();
	frame.Renderer.SetPerspective (640, 480);

	frame.Input.Mouse.SetMode (MOUSE_NORMAL);

	frame.Input.doOnUpdate (INSUpdateHandler);
	frame.Input.doOnMouseDown (INSMouseClickHandler);

	frame.Renderer.Camera.Reset (G3::VEC3F (3.5, 2.5, 3.5));
	frame.Renderer.Camera.FocusOn (G3::VEC3F (0, 0, 0));

    /* Enable smooth shading */
    glShadeModel( GL_SMOOTH );

    /* Set the background black */
    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );

    /* Depth buffer setup */
    glClearDepth( 1.0f );

    /* The Type Of Depth Test To Do */
    glDepthFunc( GL_LEQUAL );

    /* Enable 2D Texture Mapping */
    glEnable( GL_TEXTURE_2D );

    G3::MainFilesys.MountDir ("../../media/font", "");
	G3::MainFilesys.MountDir ("../../media", "");

	Font1.Load ("font.lua");
	TextBox.Font = &Font1;
	TextBox2.Font = &Font1;

	TextBox.Position = G3::VEC2I (10, 10);
	TextBox2.Position = G3::VEC2I (10, 22);

	Mat1.ambient = G3::COL4F (0.1f, 0.1f, 0.1f, 1.0f);
	Mat1.diffuse = G3::COL4F (0.65f, 0.65f, 0.65f, 1.0f);
	Mat1.specular = G3::COL4F (0.8f, 0.8f, 0.8f, 1.0f);
	Mat1.emission = G3::COL4F (0.0f, 0.3f, 0.0f, 1.0f);
	Mat1.shininess = 0.6f;
	Mat1.reflectance = 0.6f;
	Mat1.textures.push_back (G3::TextureManager.GetHandle ("textures/puzzle.png"));

	Mat2.ambient = G3::COL4F (0.1f, 0.1f, 0.1f, 1.0f);
	Mat2.diffuse = G3::COL4F (0.0f, 0.8f, 0.0f, 1.0f);
	Mat2.specular = G3::COL4F (0.8f, 0.8f, 0.8f, 1.0f);
	Mat2.emission = G3::COL4F (0.0f, 0.3f, 0.0f, 1.0f);
	Mat2.shininess = 0.6f;
	Mat2.reflectance = 0.6f;
	Mat2.textures.push_back (G3::TextureManager.GetHandle ("textures/puzzle.png"));

	Polygon1.TexCoords.push_back (G3::VEC2F (0, 0));
	Polygon1.TexCoords.push_back (G3::VEC2F (1, 0));
	Polygon1.TexCoords.push_back (G3::VEC2F (1, 1));
	Polygon1.TexCoords.push_back (G3::VEC2F (0, 1));

	Polygon1.Vertices.push_back (G3::VEC3F (1, 1, 0));
	Polygon1.Vertices.push_back (G3::VEC3F (1,-1, 0));
	Polygon1.Vertices.push_back (G3::VEC3F (0,-1, 1));
	Polygon1.Vertices.push_back (G3::VEC3F (0, 1, 1));
	Polygon1.CalcPlane ();

	FPSCounter.Start ();
	MovementTimer.Start ();

	while (true) {
		frame.Input.GetInput ();

        frame.Renderer.ClearBuffer ();

		FPSCounter.CheckTime ();

	    frame.Renderer.Look ();
		frame.Renderer.RenderGrid (1.0, G3::COL3F (0.2f, 0.2f, 0.2f), false);

		glEnable (GL_TEXTURE_2D);

		Mat1.glApply ();

		glBegin (GL_POLYGON);
			for (i=0; i<(int) Polygon1.Vertices.size (); i++) {
				glTexCoord2f (Polygon1.TexCoords [i].x, Polygon1.TexCoords [i].y);
				glVertex3f (Polygon1.Vertices [i].x,
							Polygon1.Vertices [i].y,
							Polygon1.Vertices [i].z);
			}
		glEnd ();

		Mat2.glApply ();

		glBegin (GL_POLYGON);
			for (i=0; i<(int) Frag1.Vertices.size (); i++) {
				glTexCoord2f (Frag1.TexCoords [i].x, Frag1.TexCoords [i].y);
				glVertex3f (Frag1.Vertices [i].x,
							Frag1.Vertices [i].y,
							Frag1.Vertices [i].z);
			}
		glEnd ();

		glBegin (GL_POLYGON);
			for (i=0; i<(int) Frag2.Vertices.size (); i++) {
				glTexCoord2f (Frag2.TexCoords [i].x, Frag2.TexCoords [i].y);
				glVertex3f (Frag2.Vertices [i].x,
							Frag2.Vertices [i].y,
							Frag2.Vertices [i].z);
			}
		glEnd ();

		glDisable (GL_TEXTURE_2D);

		glColor3f (1.0f, 0.1f, 0.1f);

		glBegin (GL_LINES);
			glVertex3f (I [0].Point.x, I [0].Point.y, I [0].Point.z);
			glVertex3f (I [1].Point.x, I [1].Point.y, I [1].Point.z);
		glEnd ();

		glColor3f (0.1f, 1.0f, 0.1f);

		glBegin (GL_LINES);
			glVertex3f (Ray [0].a.x, Ray [0].a.y, Ray [0].a.z);
			glVertex3f (Ray [0].b.x, Ray [0].b.y, Ray [0].b.z);
			glVertex3f (Ray [1].a.x, Ray [1].a.y, Ray [1].a.z);
			glVertex3f (Ray [1].b.x, Ray [1].b.y, Ray [1].b.z);
		glEnd ();

		glColor3f (1.0f, 1.0f, 1.0f);

		glBegin (GL_POINTS);
			glVertex3f (I [0].Point.x, I [0].Point.y, I [0].Point.z);
			glVertex3f (I [1].Point.x, I [1].Point.y, I [1].Point.z);
		glEnd ();

		TextBox.glPrint ("FPS: %lf d: %f", FPSCounter.FPS, Polygon1.Plane.GetDistance (frame.Renderer.Camera.GetWorldPosition ()));
		TextBox2.glPrint ("I1: (%.2f, %.2f, %.2f) I2: (%.2f, %.2f, %.2f)", I [0].Point.x, I [0].Point.y, I [0].Point.z, I [1].Point.x, I [1].Point.y, I [1].Point.z);

        frame.Renderer.SwapBuffer ();

		FPSCounter.AddFrame ();
	}

	Font1.Clear ();

	return 0;
}
